#ifndef GAMEENGINE_GEOMETRY_ANGLE_H
#define GAMEENGINE_GEOMETRY_ANGLE_H

#include <GameEngine/Geometry/geometry_settings.h>

#include <GameEngine/Geometry/Location/point2d.h>
#include <GameEngine/Geometry/Location/point3d.h>

#ifndef PI
//! Define the value of PI
#define PI 3.1415926535897932384626433832795
#endif



#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

namespace Angles {
	//! Angle system
	/*!
	 *  Includes radians, grades and degrees
	 */
	enum AngleSystem {
		//! Radians
		Rad = 0,
		//! Degrees
		Deg = 1,
		//! Grades
		Grad = 2
	};
}



//! Class used to represent angles
/*!
 *  An angle is represented internally using the grade angle system. However, the class
 *  proposes conversions to the radian and degree angle systems, making its use transparent
 *  in any angle system.
 */
template <class T>
class Angle {

public:
	Angle();
	Angle(const T& angle, const Angles::AngleSystem& system = Angles::Grad);
	Angle(const Angle& rhs);
	template <class R> Angle(const Angle<R>& rhs);
	virtual ~Angle();
	Angle& operator=(const Angle& rhs);
	template <class R> Angle& operator=(const Angle<R>& rhs);

	Angle operator+(const Angle& rhs);
	template <class R> Angle operator+(const Angle<R>& rhs);
	Angle& operator+=(const Angle& rhs);
	template <class R> Angle& operator+=(const Angle<R>& rhs);

	Angle operator-(const Angle& rhs);
	template <class R> Angle operator-(const Angle<R>& rhs);
	Angle& operator-=(const Angle& rhs);
	template <class R> Angle& operator-=(const Angle<R>& rhs);

	Angle operator*(const T& rhs);
	Angle& operator*=(const T& rhs);
	Angle operator/(const T& rhs);
	Angle& operator/=(const T& rhs);

	bool operator<(const Angle& rhs) const;
	bool operator<=(const Angle& rhs) const;
	bool operator>(const Angle& rhs) const;
	bool operator>=(const Angle& rhs) const;

	bool operator==(const Angle& rhs) const;
	bool operator!=(const Angle& rhs) const;

	T angle(const Angles::AngleSystem& system) const;
	T deg() const;
	T rad() const;
	T grad() const;
	void set_angle(const T& angle, const Angles::AngleSystem& system);
	void set_deg(const T& angle);
	void set_rad(const T& angle);
	void set_grad(const T& angle);

	Angle& reduce_angle();

	static T grad_to_deg(const T& grad_angle);
	static T grad_to_rad(const T& grad_angle);
	static T rad_to_deg(const T& rad_angle);
	static T rad_to_grad(const T& rad_angle);
	static T deg_to_rad(const T& deg_angle);
	static T deg_to_grad(const T& deg_angle);

	static Angle<T> from_points(const Point3d<T>& p0, const Point3d<T>& p1, const Point3d<T>& p2);
	static Angle<T> from_points(const Point2d<T>& p0, const Point2d<T>& p1, const Point2d<T>& p2);

protected:
	//! Angle value
	T grad_;
};

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#include <GameEngine/Geometry/Core/angle.hpp>

#endif
